#include <iostream>
#include <string>
#include <map>
#include <cassert>
#include <boost/shared_ptr.hpp>

typedef std::map<std::string, std::string> properties;

// plugin base class (interface)
class plugin
{
public:
    virtual ~plugin() {};
    
    // the actual plugin interface goes here
    virtual void implement_me() = 0;
};

// every plugin must have a corresponding factory
class plugin_factory
{
public:
    virtual ~plugin_factory() {};

    // returns a new plugin instance
    virtual plugin* create(const properties& pmap) = 0;
};

// the plugin registry
// holds factories for all registered plugins
class plugin_manager
{
public:
    // register a plugin factory
    // any previously registered factory with the same unique identifier
    // will be removed!
    void register_plugin(boost::shared_ptr<plugin_factory> factory, 
                         const std::string& identifier) 
    {
        factories[identifier] = factory;
    }
    
    //TODO: add method void deregister_plugin(std::string unique_identifier)
    
    // returns a new plugin of some type specified by the unique identifier.
    // returns 0 (NULL) if no such plugin type is registered.
    plugin* create_instance(std::string ident, const properties& pmap) 
    {
        std::map<std::string, boost::shared_ptr<plugin_factory> >::iterator it;
        it = factories.find(ident);
        
        if(it == factories.end()) 
        {
            return 0;
        } else {
            return it->second.get()->create(pmap);
        }
    }


private:
    std::map<std::string, boost::shared_ptr<plugin_factory> > factories;
};

// bogus plugin implementations with corresponding factories follow
class plugin1 : public plugin
{
public:
    virtual void implement_me() 
    {
        std::cout << "i am plugin 1" << std::endl;
    }
};

class plugin1_factory : public plugin_factory
{
public:
    virtual plugin* create(const properties& pmap) 
    {
        return new plugin1();
    }
};

class plugin2 : public plugin
{
public:
    // we have a custom constructor here to handle some property
    
    plugin2(std::string funky_property) 
        : prop(funky_property)
    {
    }

    virtual void implement_me() 
    {
        std::cout << "i am plugin 2 and my funky property is: " 
                  << prop 
                  << std::endl;
    }
private:
    std::string prop;
};

class plugin2_factory : public plugin_factory
{
public:
    virtual plugin* create(const properties& pmap) 
    {
        properties::const_iterator it = pmap.find("funky");
        if(it == pmap.end()) {
            return new plugin2("not set!");
        } else {
            return new plugin2(it->second);
        }
    }
};

int main()
{
    // usage demo!
    
    // this fat bloke holds the factories
    plugin_manager pman;

    // always wrap factories in shared pointers!
    pman.register_plugin(boost::shared_ptr<plugin_factory>(new plugin1_factory()), "plugin1");
    pman.register_plugin(boost::shared_ptr<plugin_factory>(new plugin2_factory()), "plugin2");

    properties plugin1_props;
    plugin* p1 = pman.create_instance("plugin1", plugin1_props);
    if(p1) {
        p1->implement_me();
        delete p1;
    }
    
    properties plugin2_props;
    plugin2_props["funky"] = "mama";
    plugin* p2 = pman.create_instance("plugin2", plugin2_props);
    if(p2) {
        p2->implement_me();
        delete p2;
    }

    properties plugin3_props;
    plugin* p3 = pman.create_instance("bogusman", plugin3_props);
    assert(p3 == 0);
    return 0;
}
